/*
 * Copyright (c) 2007, John Hurliman
 * All rights reserved.
 *
 * - Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * - Neither the name of the Second Life Reverse Engineering Team nor the names
 *   of its contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using libsecondlife;
using libsecondlife.Rendering;

namespace sceneviewer.Prims
{
    public class PrimVisual
    {
        // Constants
        public const int MaxCut = 200;

        public Matrix Matrix;
        public PrimMesh Mesh;
        public VertexPositionColor[] VertexArray;
        public int[] IndexArray;
        public Primitive Prim;
        public BoundingBox BoundBox;

        protected Color color;

        protected List<VertexPositionColor> Vertices;

        public PrimVisual(Primitive prim)
        {
            Prim = prim;

            BuildVertices();

            VertexArray = Vertices.ToArray();
            BuildMatrix();
        }

        public void Select()
        {
            BuildVertices();
        }

        public void Deselect()
        {
            ;
        }

        public void Update(ObjectUpdate primUpdate)
        {
            Prim.Position = primUpdate.Position;
            Prim.Rotation = primUpdate.Rotation;
            Prim.Acceleration = primUpdate.Acceleration;
            Prim.Velocity = primUpdate.Velocity;
            Prim.AngularVelocity = primUpdate.AngularVelocity;

            BuildMatrix();
        }

        private void BuildVertices()
        {
            Mesh = Render.GenerateMesh(Prim.Data, Render.DETAIL_LEVELS[Render.DETAIL_LEVELS.Length - 1]);
            Vertices = new List<VertexPositionColor>(Mesh.Vertices.Count);

            // TODO: This is temporary, for debugging and entertainment purposes
            Random rand = new Random((int)Prim.LocalID + Environment.TickCount);
            byte r = (byte)rand.Next(256);
            byte g = (byte)rand.Next(256);
            byte b = (byte)rand.Next(256);
            color = new Color(r, g, b);
            //color = Color.Red;

            for (int i = 0; i < Mesh.Vertices.Count; i++)
            {
                LLVector3 point = Mesh.Vertices[i];
                Vertices.Add(new VertexPositionColor(new Vector3(point.X, point.Y, point.Z), color));
            }

            IndexArray = Mesh.Indices.ToArray();
        }

        private void BuildMatrix()
        {
            Matrix offset = Matrix.CreateTranslation(new Vector3(Prim.Position.X, Prim.Position.Y, Prim.Position.Z));
            Matrix rotation = Matrix.CreateFromQuaternion(new Quaternion(Prim.Rotation.X, Prim.Rotation.Y, Prim.Rotation.Z,
                Prim.Rotation.W));
            Matrix scaling = Matrix.CreateScale(Prim.Scale.X, Prim.Scale.Y, Prim.Scale.Z);

            Matrix = scaling * rotation * offset;
            
            // Now that we have the final transformation matrix we can create a proper bounding box
            // TODO: This code has only been tested with linear extrusion prims
            if (Prim.ParentID != 0)
            {
                float minX = -0.5f, minY = -0.5f, minZ = -0.5f;
                if (Prim.Data.PathShearX < 0) minX *= Prim.Data.PathShearX;
                if (Prim.Data.PathShearY < 0) minY *= Prim.Data.PathShearY;

                float maxX = 0.5f, maxY = 0.5f, maxZ = 0.5f;
                if (Prim.Data.PathShearX > 0) maxX *= Prim.Data.PathShearX;
                if (Prim.Data.PathShearY > 0) maxY *= Prim.Data.PathShearY;

                Vector3 min = Vector3.Transform(new Vector3(minX, minY, minZ), Matrix);
                Vector3 max = Vector3.Transform(new Vector3(maxX, maxY, maxZ), Matrix);

                BoundBox = new BoundingBox(min, max);
            }
            else
            {
                BoundBox = new BoundingBox();
            }
        }
    }
}
